package com.audaque.springboot.foshanupload.gateway.filter;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.audaque.springboot.foshanupload.authcore.constants.AuthConst;
import com.audaque.springboot.foshanupload.authcore.model.currentUser.TransmittableThreadLocalCurrentUser;
import com.audaque.springboot.foshanupload.authcore.properties.LoginProperties;
import com.audaque.springboot.foshanupload.core.enums.ResultCodeEnum;
import com.audaque.springboot.foshanupload.core.properties.StaticResourcePathProperties;
import com.audaque.springboot.foshanupload.core.result.R;
import com.audaque.springboot.foshanupload.jwtcore.component.iface.JwtProviderIface;
import com.audaque.springboot.foshanupload.jwtcore.constants.JwtConst;
import com.audaque.springboot.foshanupload.jwtcore.properties.JwtProperties;
import com.audaque.springboot.foshanupload.jwtcore.util.encode.rsaPem.RsaPem;
import com.audaque.springboot.foshanupload.jwtcore.util.encode.rsaPem.RsaUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebSession;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.List;
import java.util.Map;

/**
 * WebSession转传TransmittableThreadLocalCurrentUser
 * 过滤/*,排除/authentication/登录，注册，登出，静态资源
 */
@Slf4j
@Component
public class GlobalJwtFilter implements GlobalFilter, Ordered {
  @Autowired
  private JwtProviderIface jwtProviderIface;

  @Autowired
  private JwtProperties jwtProperties;


  @Value("${db.tenantId}")
  private String tenantId;
  @Autowired
  private LoginProperties loginProperties;
  @Autowired
  private StaticResourcePathProperties staticResourcePathProperties;

  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
	log.debug("GlobalJwtFilter:执行前置过滤器逻辑:" + Thread.currentThread().getName());


	ServerHttpRequest request = exchange.getRequest();
	ServerHttpResponse response = exchange.getResponse();
	Mono<WebSession> sessionMono = exchange.getSession();
	MultiValueMap<String, String> queryParams = request.getQueryParams();


	//要排除的路径
	List<String> excludePathPatterns = staticResourcePathProperties.getList();
	List<String> unAuthenticatedUrlList = loginProperties.getUnAuthenticatedUrlList();
	unAuthenticatedUrlList.addAll(excludePathPatterns);
	String excludedUris = String.join(",", unAuthenticatedUrlList);


	URI uri = request.getURI();
	String url = uri.toString();
	log.debug("url：" + url);

	String path = uri.getPath();
	log.debug("path：" + path);
	int i = path.indexOf("/");
	String[] split = path.split("/");
	int length = split[1].length();
	String substring = path.substring(i + length + 1);

	//不在白名单内要处理
	if (!excludedUris.contains(substring)) {
	  String accessToken = null;
	  String accessTokenKey = jwtProperties.getAccessTokenKey();
	  List<String> accessTokenKeys = request.getHeaders().get(accessTokenKey);
	  accessTokenKeys = ListUtils.emptyIfNull(accessTokenKeys);
	  if (!accessTokenKeys.isEmpty()) {
		accessToken = (String) accessTokenKeys.get(0);
	  }
	  //没有则放行
	  if (StringUtils.isBlank(accessToken)) {
		return handlerNoPass(response);
	  }

	  //每当用户想要访问受保护的路由或资源时，用户将使用承载【bearer】模式发送JWT，通常在Authorization标头中。标题的内容应如下所示： Authorization: Bearer <token>
	  //使用token时手动添加前缀
	  if (!accessToken.startsWith(JwtConst.ACCESSTOKEN_STARTS_WITH)) {
		return handlerNoPass(response);
	  }

	  //去除Bearer 后部分
	  accessToken = accessToken.substring(7);

	  if (StringUtils.isBlank(accessToken)) {
		return handlerNoPass(response);
	  }


	  //解密token，拿到里面的对象claims
	  String body = null;
	  try {
		body = jwtProviderIface.getBody(accessToken);
	  } catch (Exception e) {
		return handlerNoPass(response);
	  }
	  if (StringUtils.isBlank(body)) {
		return handlerNoPass(response);
	  }
	  //无状态
	  TransmittableThreadLocalCurrentUser transmittableThreadLocalCurrentUser = JSONUtil.toBean(body, TransmittableThreadLocalCurrentUser.class);
	  transmittableThreadLocalCurrentUser.setAccessToken(accessToken);

	  String enId = transmittableThreadLocalCurrentUser.getId();
	  String enUsername = transmittableThreadLocalCurrentUser.getUsername();
	  String privateKey = jwtProperties.getPrivateKey();
	  String deId = null;
	  String deUsername = null;
	  try {
		RsaUtil rsa = new RsaUtil(RsaPem.FromPEM(privateKey));
		//解密userId
		deId = rsa.Decode(enId);
		deUsername = rsa.Decode(enUsername);
	  } catch (Exception e) {
		e.printStackTrace();
		log.error(String.format("jwt:claims:id:解密失败：%s", e.getMessage()));
		return handlerNoPass(response);
	  }
	  transmittableThreadLocalCurrentUser.setId(deId);
	  transmittableThreadLocalCurrentUser.setTenantId(tenantId);
	  transmittableThreadLocalCurrentUser.setUsername(deUsername);

	  //刷新token
	  //jwt的负载要加密
	  Map<String, Object> map = BeanUtil.beanToMap(transmittableThreadLocalCurrentUser);
	  map.put("verify", "verify");
	  String accessToken1 = null;
	  try {
		accessToken1 = jwtProviderIface.getAccessToken(map);
	  } catch (Exception e) {
		throw new RuntimeException(e);
	  }

	  String refreshTokenKey = jwtProperties.getRefreshTokenKey();

	  //静态初始化
	  String[] headerValues1 = {new String(accessToken1)};
	  String transmittableThreadLocalCurrentUserStr = JSONUtil.toJsonStr(transmittableThreadLocalCurrentUser);
	  //内部传递当前用户，使用简单的算法base64可压缩数据
	  byte[] b = Base64Utils.encode(transmittableThreadLocalCurrentUserStr.getBytes());
	  String[] headerValues2 = {new String(b)};


	  ServerHttpRequest build = exchange.getRequest()
	  .mutate()
	  //设置header
	  .header(refreshTokenKey, headerValues1)
	  .header(AuthConst.FROM_GATEWAY_CURRENTUSER_HEADER_KEY, headerValues2)
	  .build();
	  //更新header
	  ServerWebExchange newExchange = exchange.mutate().request(build).build();


	  //放行
	  return chain.filter(newExchange).then(Mono.fromRunnable(() -> {
		log.debug("GlobalJwtFilter:执行后置过滤器逻辑:" + Thread.currentThread().getName());

	  }));

	}
	//放行
	return chain.filter(exchange).then(Mono.fromRunnable(() -> {
	  log.debug("GlobalJwtFilter:执行后置过滤器逻辑:" + Thread.currentThread().getName());

	}));
  }


  @Override
  public int getOrder() {
	return -9;
  }


  private Mono<Void> handlerNoPass(ServerHttpResponse response) {
	R r = new R();
	r.setSuccess(false);
	r.setCode(ResultCodeEnum.NOT_LOGIN.getCode());
	r.setMsg(ResultCodeEnum.NOT_LOGIN.getMsg());
	//响应
	//  Mono<Void> writeWith(Publisher<? extends DataBuffer> var1);
	return response.writeWith(
	Mono.just(response.bufferFactory().wrap(
	//DataBuffer
	JSONUtil.toJsonStr(r).getBytes()
	)
	)

	);


  }


}
